<!--  -->
<template>
<div class='sh-upload'>
  <upload-list v-if="type === 'list' || type === 'pictureCard'"
  :fileList="fileList"
  @on-file-remove="handleRemove"
  @on-file-preview="handlePreview">
  </upload-list>
  <div :class="uploadClass"
    v-show="limit ? fileList.length < limit : true"
    @click="uploadHandleClick"
    @drop.prevent="onDrop"
    @paste='handlePaste'
    @dragover.prevent="dragOver = true"
    @dragleave.prevent="dragOver = false">
    <input
    type="file"
    ref="uplodeInput"
    name="fileUplode"
    class="sh-upload-input"
    @change="uploadInputChange"
    :multiple="multiple || type === 'pictureCard'"
    :accept="accept">
    <slot><i class="iconfont icon-add1"></i></slot>
  </div>
  <slot name="tips"></slot>
</div>
</template>

<script>
import ajax from './ajax'
import { oneOf } from '../../../utils/assist'
import Emitter from '../../../mixins/emitter'
import UploadList from './UploadList'
export default {
  // import引入的组件需要注入到对象中才能使用
  name: 'Upload',
  mixins: [ Emitter ],
  components: { UploadList },
  props: {
    type: {
      type: String,
      validator (value) {
        return oneOf(value, ['select', 'drag', 'list', 'pictureCard'])
      },
      default: 'select'
    },
    actions: {
      type: String,
      required: true
    },
    limit: Number,
    headers: {
      type: Object,
      default () {
        return {}
      }
    },
    data: {
      type: Object
    },
    name: {
      type: String,
      default: 'file'
    },
    withCredentials: {
      type: Boolean,
      default: false
    },
    multiple: {
      type: Boolean,
      default: false
    },
    format: {
      type: Array,
      default () {
        return []
      }
    },
    accept: {
      type: String
    },
    maxSize: {
      type: Number
    },
    beforeUpload: Function,
    onProgress: {
      type: Function,
      default () {
        return {}
      }
    },
    onSuccess: {
      type: Function,
      default () {
        return {}
      }
    },
    onError: {
      type: Function,
      default () {
        return {}
      }
    },
    onRemove: {
      type: Function,
      default () {
        return {}
      }
    },
    onPreview: {
      type: Function,
      default () {
        return {}
      }
    },
    onStart: Function, // 冗余方法
    onExceed: Function,
    onFormatError: {
      type: Function,
      default () {
        return {}
      }
    },
    defaultFileList: {
      type: Array,
      default () {
        return []
      }
    },
    paste: {
      type: Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    }
  },
  data () {
    // 这里存放数据
    return {
      dragOver: false,
      tempIndex: 1,
      fileList: [] // 已上传的文件列表
    }
  },
  // 监听属性 类似于data概念
  computed: {
    uploadClass () {
      return [
        `${['sh-upload']}`,
        {
          [`${'sh-upload'}-select`]: this.type === 'select',
          [`${'sh-upload'}-drag`]: this.type === 'drag',
          [`${'sh-upload'}-dragOver`]: this.type === 'drag' && this.dragOver,
          [`${'sh-upload'}-picture-card`]: this.type === 'pictureCard'
        }
      ]
    }
  },
  // 监控data中的数据变化
  watch: {
    defaultFileList: {
      //  以上传的文件列表
      immediate: true,
      handler (fileList) {
        // if (fileList.length > 0) {
        //   this.fileList = fileList.map(item => {
        //     item.status = 'finished'
        //     item.percentage = 100
        //     item.uid = Date.now() + this.tempIndex++
        //     return item
        //   })
        // }
        fileList.forEach(f => {
          this.fileList.push({
            ...f, // 将传入对象混入当前对象
            src: f.url,
            uid: Date.now() + this.tempIndex++,
            status: 'finished',
            showProgress: false,
            percentage: 100,
            isDefault: true
          })
        })
      }
    },
    fileList: {
      deep: true,
      handler: function (val) {
        if (val.length < 1) {
          this.imgList = []
        }
      }
    }
  },
  // 方法集合
  methods: {
    uploadHandleClick (e) {
      if (this.disabled) return
      this.$refs.uplodeInput.value = null
      this.$refs.uplodeInput.click()
    },
    onDrop (e) {
      this.dragOver = false
      if (this.disabled) return
      this.uploadFiles(e.dataTransfer.files)
    },
    handlePaste (e) {
      if (this.disabled) return
      if (this.paste) {
        this.uploadFiles(e.clipboardData.files)
      }
    },
    uploadInputChange (e) {
      const files = e.target.files
      if (!files) return
      this.uploadFiles(files)
    },
    uploadFiles (files) {
      if (this.limit && this.fileList.length + files.length > this.limit) {
        this.onExceed && this.onExceed(files, this.fileList)
        return
      }
      let postFiles = Array.prototype.slice.call(files)
      if (!this.multiple) { postFiles = postFiles.slice(0, 1) }
      if (postFiles.length === 0) return
      postFiles.forEach(file => {
        if (this.onStart) { this.onStart(file) }
        this.upload(file)
      })
    },
    upload (file) {
      if (!this.beforeUpload) {
        return this.post(file)
      }
      const before = this.beforeUpload(file)
      if (before && before.then) {
        before.then(processedFile => {
          if (Object.prototype.toString.call(processedFile) === '[object File]') {
            this.post(processedFile)
          } else {
            this.post(file)
          }
        }, () => {
          this.onRemove(null, file)
        })
      } else if (before !== false) {
        this.post(file)
      }
    },
    post (file) {
      // 文件流的格式校验
      if (this.format.length) {
        const _fileFormat = file.name.split('.').pop().toLocaleLowerCase()
        const checked = this.format.some(item => item.toLocaleLowerCase() === _fileFormat)
        if (!checked) {
          this.onFormatError(file, this.fileList)
          return false
        }
      }
      // 校验文件大小是否超长
      if (this.maxSize) {
        if (file.size > this.maxSize * 1024) {
          this.onExceed(file, this.fileList)
          return false
        }
      }
      this.fileStart(file)
      let formData = new FormData()
      formData.append(this.name, file)
      ajax({
        headers: this.headers,
        withCredentials: this.withCredentials,
        file: file,
        data: this.data,
        filename: this.name,
        action: this.actions,
        onProgress: e => {
          this.handleProgress(e, file)
        },
        onSuccess: res => {
          this.handleSuccess(res, file)
        },
        onError: (err, response) => {
          this.handleError(err, response, file)
        }
      })
    },
    fileStart (file) {
      file.uid = Date.now() + this.tempIndex++
      const _file = {
        status: 'uploading',
        name: file.name,
        size: file.size,
        percentage: 0,
        uid: file.uid,
        file: file,
        showProgress: true
      }
      this.showImg(_file)
      this.fileList.push(_file)
    },
    getFile (file) {
      const fileList = this.fileList
      let target
      fileList.every(item => {
        target = file.uid === item.uid ? item : null
        return !target
      })
      return target
    },
    handleProgress (e, file) {
      const _file = this.getFile(file)
      this.onProgress(e, _file, this.fileList)
      _file.percentage = e.percent || 0
    },
    handleSuccess (res, file) {
      const _file = this.getFile(file)
      if (_file) {
        _file.status = 'finished'
        _file.response = res
        _file.showProgress = false
        this.onSuccess(res, _file, this.fileList)
        this.dispatch('FormItem', 'on-form-change', _file)
      }
    },
    handleError (err, response, file) {
      const _file = this.getFile(file)
      const fileList = this.fileList
      _file.status = 'fail'
      _file.showProgress = false
      fileList.splice(fileList.indexOf(_file), 1)
      // this.clearFiles()
      this.onError(err, response, file)
    },
    handleRemove (file) {
      const fileList = this.fileList
      fileList.splice(fileList.indexOf(file), 1)
      this.onRemove(file, fileList)
    },
    handlePreview (file) {
      if (file.status === 'finished') {
        this.onPreview(file)
      }
    },
    clearFiles () {
      this.fileList = []
    },
    showImg (item) {
      if (!item.src) {
        const fr = new FileReader()
        fr.readAsDataURL(item.file)// 读取文件
        fr.onload = e => {
          this.getFile(item).src = e.target.result
        }
      }
    }
  }
}
</script>
